# coding: utf-8

import copy
from uuid import uuid4
from collections import defaultdict

from django.db.models import Q
from django.conf import settings
from django.shortcuts import get_object_or_404
from django.core.urlresolvers import reverse
from django.shortcuts import redirect

from ddutils.views import DDListView, DDDetailView
from ddutils.utils import search_filed, VarDict, TaskRunValidate
from .models import Task, Category, TaskLog, TaskConf, TaskInputParams, TaskHostLog
from .forms import TaskConfForm
from task_engine.tasks import run_task
from task_engine import constants as task_status


class TaskListView(DDListView):
    model = Task
    queryset = Task.objects.all()
    template_name = "jobs/task_list.html"
    serializer_fields = ('name', 'type', 'category', 'id')

    def get_context_data(self, request, *args, **kwargs):
        categories = [category for category in Category.objects.all()]
        types = ['shell', 'playbook']
        return {'categories': categories, 'types': types}

    def searializer_data(self, pagination):
        tasks = []
        for task in pagination['result']:
            category = Category.objects.get(id=task.category)
            task.category = category.display
            tasks.append(task)
        pagination['result'] = tasks
        return super(TaskListView, self).searializer_data(pagination)

    def get_queryset(self, request):
        queryset = self.queryset
        type = request.GET.get('type', None)
        name = request.GET.get('name', None)
        category = request.GET.get('category', None)

        if category:
            try:
                category = Category.objects.get(name=category)
            except Category.DoesNotExist:
                category = None
            if category:
                queryset = queryset.filter(category=category.id)
        if type in settings.TASK_TYPES.values():
            queryset = queryset.filter(type=type)

        if name:
            queryset = queryset.filter(Q(name__contains=name))

        return queryset.all()


class TaskDetailView(DDDetailView):
    model = Task
    template_name = "jobs/task_detail.html"


class TaskEditView(DDDetailView):
    form = TaskConfForm
    model = Task
    template_name = "jobs/task_edit.html"

    def get_context_data(self, request, pk):
        task_conf = TaskConf.objects.filter(task_id=pk).first()
        if not task_conf:
            task_conf = TaskConf(task_id=pk)
            task_conf.save()
        params = [param for param in TaskInputParams.objects.filter(task_id=pk).all()]
        return {'conf': task_conf, 'params': params, 'params_count': len(params)}

    def post(self, request, pk):
        '''
        验证task是否存在
        验证task_conf是否存在, 不孙在则创建
        验证带的参数是否正常,正常则保存
        如果成功则跳转到业务详情页面
        如果失败则还继续停留在当前编辑页面
        :param request:
        :param pk:
        :return:
        '''
        task = get_object_or_404(self.model, pk=pk)
        conf = self.get_context_data(request, pk)['conf']
        form = TaskConfForm(data=request.POST, instance=conf)
        if form.is_valid():
            conf = form.save(commit=False)
            conf.last_modify_user = request.user.username
            conf.save()
            self.vars_save(request, task)
            if request.POST.get('desc', None):
                task.desc = request.POST['desc']
                task.save()
        return redirect(reverse('task_detail', kwargs={'pk': pk}))

    @staticmethod
    def vars_save(request, task):
        data = copy.deepcopy(request.POST)
        params = defaultdict(VarDict)
        for k, v in data.items():
            field, index = search_filed(k)
            if not field or not index:
                continue
            params[index][field] = v
        exits_param_ids = set([param.id for param in TaskInputParams.objects.filter(task_id=task.id).all()])
        save_params = []
        for k, v in params.items():
            v['task_id'] = task.id
            print v
            param = TaskInputParams.objects.filter(task_id=task.id).filter(name=v['name']).first()
            if not param:
                param = TaskInputParams(**v)
            else:
                param.desc = v.get('desc', '')
                param.type = v.get('type', 'input')
                param.default = v.get('default', '')
                param.required = v.get('required', False)
            param.last_modify_user = request.user.username
            param.save()
            save_params.append(param.id)
        delete_params = exits_param_ids.difference(save_params)
        for id in delete_params:
            try:
                param = TaskInputParams.objects.get(id=id)
                param.delete()
            except TaskInputParams.DoesNotExist:
                pass
        return save_params


class TaskLogListView(DDListView):
    model = TaskLog
    queryset = TaskLog.objects
    serializer_fields = ('create_time', 'update_time', 'celery_task_id', 'desc', 'status', 'id')

    def get_queryset(self, request):
        task_id = request.GET.get('task_id', 0)
        return self.queryset.filter(task_id=task_id).all()


class TaskRunView(DDDetailView):
    model = Task
    template_name = "jobs/task_run.html"

    def get_context_data(self, request, pk):
        task_conf = TaskConf.objects.filter(task_id=pk).first()
        if not task_conf:
            task_conf = TaskConf(task_id=pk)
            task_conf.save()
        params = [param for param in TaskInputParams.objects.filter(task_id=pk).all()]
        return {'conf': task_conf, 'params': params, 'params_count': len(params)}

    def post(self, request, pk):
        task = get_object_or_404(self.model, pk=pk)
        run_validate = TaskRunValidate(request, task)
        if run_validate.is_validate():
            task_log = TaskLog(**run_validate.model_data)
            task_log.save()
            # send task to celery
            celery_id = '{}'.format(uuid4())
            task_log_id = task_log.id
            run_task.apply_async(args=(task_log_id, celery_id), task_id=celery_id)
            return redirect(reverse('task_detail', kwargs={'pk': pk}))
        return redirect(reverse('task_run', kwargs={'pk': pk}))


class TaskLogDetailView(DDDetailView):
    model = TaskLog
    template_name = "jobs/task_log.html"

    def get_context_data(self, request, pk):
        host_tasks = [host_task for host_task in TaskHostLog.objects.filter(task_log_id=pk).all()]
        query_set = TaskHostLog.objects.filter(task_log_id=pk)
        success_count = query_set.filter(status=task_status.SUCCESS).count()
        failure_count = query_set.filter(status=task_status.FAILURE).count()
        return {'host_tasks': host_tasks, 'success_count': success_count, 'failure_count': failure_count}